#include "cepollclientdatamgr.h"
#include "cglobal.h"
#include "cclientdata.h"
#include "cpublicdata.h"
#include "clogoper.h"

#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QCoreApplication>

const int g_nReadMaxCount = 10; //读取五十次

CClientProcess::CClientProcess(int nClientFd, QObject *parent)
    : QObject(parent)
{
    m_nClientFd = nClientFd;
    m_pClient = nullptr;
    m_nPort = 0;
    m_bRecvConnected = false;
    m_nReplyByte0 = CPublicData::instance().m_stServerCfg.m_nReplyByte0;
    m_nReplyByte1 = CPublicData::instance().m_stServerCfg.m_nReplyByte1;
}

void CClientProcess::setCfg(QHostAddress addr, quint16 nPort)
{
    m_addr = addr;
    m_nPort = nPort;
    m_sGuid = addr.toString() + ":" + QString::number(nPort);
}

void CClientProcess::appendRecvData(QByteArray &byteIn)
{
    QMutexLocker locker(&m_mutex);
    m_byteBuffer.append(byteIn);
    if (m_byteBuffer.size() < 8) return;

    int nIndex = 0; //报文起始
    int nCount = 0; //循环次数记数
    while (nCount < g_nReadMaxCount)
    {
        if (true == m_byteBuffer.isEmpty()) break;
        //找报文头
        while (CKYTXHeader::enHeader1 != (quint8)m_byteBuffer[nIndex]
               || CKYTXHeader::enHeader2 != (quint8)m_byteBuffer[nIndex + 1]
               || CKYTXHeader::enHeader3 != (quint8)m_byteBuffer[nIndex + 2]
               || CKYTXHeader::enHeader4 != (quint8)m_byteBuffer[nIndex + 3])
        {
            nIndex++;
            if (nIndex + 4 > m_byteBuffer.size()) return;
            continue;
        }
        //长度不足报文头长度则舍弃
        if (m_byteBuffer.size() - nIndex < (int)sizeof(CKYTXHeader)) break;
        //裁减报文头之前的数据
        if (nIndex > 0) m_byteBuffer.remove(0, nIndex);
        //转为报文头格式
        CKYTXHeader* pHeader = new CKYTXHeader;
        memcpy(pHeader, m_byteBuffer, sizeof(CKYTXHeader));

        //缓存报文长度不足报文头规定的长度即返回继续接收
        int nSize = sizeof(CKYTXHeader) + pHeader->getDataLength();
        if (m_byteBuffer.size() < nSize)
        {
            SAFE_DELETE(pHeader);
            break;
        }

#if 1
        //输出分析
        QByteArray byteLog = m_byteBuffer.mid(0, nSize + sizeof(CKYTXHeader));
        QString sDataMsg;
        for (int i = 0; i < byteLog.size(); i++)
        {
            if (i > 0)
                sDataMsg += " " + QString("%1").arg((quint8)byteLog[i], 2, 16, QLatin1Char('0'));
            else
                sDataMsg += QString("%1").arg((quint8)byteLog[i], 2, 16, QLatin1Char('0'));
        }
        addLogMsg(enWorkInfo, sDataMsg);
#endif

        //解析报文
        QByteArray byteSingleData = m_byteBuffer.mid(sizeof(CKYTXHeader), nSize);
        int nRtn = processData(byteSingleData, pHeader);
        SAFE_DELETE(pHeader);
        //解析成功就裁掉解析的部分，不成功直接退出等待下一次接收
        if (enSuccess == nRtn || enFail == nRtn)
        {
            m_byteBuffer.remove(0, nSize);
        }
        else
        {
            break;
        }
        nCount++;
    }
}

int CClientProcess::processData(QByteArray &byteData, CKYTXHeader *pHeader)
{
    //判断包体长度，长度不足则继续等待下一包
    quint32 nDataLen = pHeader->getDataLength();
    if (byteData.size() < nDataLen)
    {
        return enWait;
    }

    //判断操作类型
    int nCommand = (int)pHeader->nCommand;
    int nRtn = enUnknown;
    switch (nCommand)
    {
    case CKYTXBase::enCmdTags:
        nRtn = processDataTags(byteData, pHeader);
        break;
    case CKYTXBase::enCmdRealData:
        nRtn = processDataRealData(byteData, pHeader);
        break;
    case CKYTXBase::enCmdWarning:
        nRtn = processDataWarning(byteData, pHeader);
        break;
    case CKYTXBase::enCmdStatus:
        nRtn = processDataStatus(byteData, pHeader);
        break;
    case CKYTXBase::enCmdHistory:
        nRtn = processDataHistory(byteData, pHeader);
        break;
    case CKYTXBase::enCmdLog:
        nRtn = processDataLog(byteData, pHeader);
        break;
    default:
        nRtn = sendRecvStatus(enFail);
        break;
    }
    return nRtn;
}

int CClientProcess::processDataTags(QByteArray &byteData, CKYTXHeader* pHeader)
{
    //判断入参
    if (m_pClient == nullptr || true == byteData.isEmpty())
        return sendRecvStatus(enFail);
    quint8 nFunc = (quint8)byteData.at(0);    //功能码

    //点表校验
    if (nFunc == CKYTXBase::enCmdTagsCheck)
    {
        CKYTXTagsCheck* pTagsCheck = new CKYTXTagsCheck;
        if (byteData.size() < (int)sizeof(CKYTXTagsCheck))
        {
            addLogMsg(enAlarm, A2T("客户端 %1 校验本地点表失败，长度不足").arg(m_pClient->m_sName));
            return sendRecvStatus(enFail);
        }
        memcpy(pTagsCheck, byteData, sizeof(CKYTXTagsCheck));

        m_pClient->m_pMutex->lock();
        quint16 nLocalCheckCode = m_pClient->m_nCRC;
        int nLocalTagsNum = m_pClient->m_nPointCount;
        m_pClient->m_pMutex->unlock();

        quint16 nClientCheckCode = pTagsCheck->getCheckCode();    //校验码
        int nClientTagsNum = (int)pTagsCheck->getTagsNum();       //测点数量
        SAFE_DELETE(pTagsCheck);
        if (nLocalTagsNum == nClientTagsNum && nLocalCheckCode == nClientCheckCode)
        {
            addLogMsg(enWorkInfo, A2T("客户端 %1 校验本地点表成功,数量:%2").arg(m_pClient->m_sName).arg(nClientTagsNum));
            //return sendRecvStatus(enSuccess);
            QString sMsg = A2T("点表校验成功");
            return sendRecvStatus(enSuccess, CKYTXBase::enCmdTags, sMsg);
        }
        else
        {
            addLogMsg(enAlarm, A2T("客户端 %1 校验本地点表错误,即将更新点表").arg(m_pClient->m_sName));
            addLogMsg(enAlarm, A2T("本地校验码 %1，客户端 %2").arg(nLocalCheckCode).arg(nClientCheckCode));
            //return sendRecvStatus(enFail);
            QString sMsg = A2T("点表校验失败");
            return sendRecvStatus(enFail, CKYTXBase::enCmdTags, sMsg);
        }
    }
    //点表信息
    else if (nFunc == CKYTXBase::enCmdTagsInfo)
    {
#if DEF_TEST == 0
        QString sMsg = A2T("接收点表");
        sendRecvStatus(enSuccess);
#endif
        //校验包长度
        if (byteData.size() < (int)sizeof(CKYTXTagsInfo))
        {
            addLogMsg(enAlarm, A2T("客户端 %1 更新本地点表失败，长度不足").arg(m_pClient->m_sName));
            return enFail;
        }

        //获取当前包信息
        CKYTXTagsInfo* pTagsInfo = new CKYTXTagsInfo;
        memcpy(pTagsInfo, byteData, sizeof(CKYTXTagsInfo));
        byteData.remove(0, sizeof(CKYTXTagsInfo));
        //处理报文
        handleTagListChange(byteData, pHeader, pTagsInfo);
        SAFE_DELETE(pTagsInfo);
#if DEF_TEST == 1
        QString sMsg = A2T("接收点表");
        sendRecvStatus(enSuccess, CKYTXBase::enCmdTags, sMsg);
#endif
        return enSuccess;
    }
    return enFail;
}

int CClientProcess::handleTagListChange(QByteArray &byteData, CKYTXHeader *pHeader, CKYTXTagsInfo *pInfo)
{
    Q_UNUSED(pHeader)
    qint16 nRemainNum = pInfo->getRemainNum();     //剩余发送数

    QString sData;
    int nRowCount = 0;
    while(false == byteData.isEmpty())
    {
        quint16 nLineLen = getQuint16(byteData.mid(0, 2));
        if (byteData.size() < nLineLen) break;
        byteData.remove(0, 2);
        sData += QString(byteData.mid(0, nLineLen)) + "\n";
        byteData.remove(0, nLineLen);
        nRowCount++;
    }
    //addLogMsg(enAlarm, A2T("客户端 %1 接收点表，行数 %2").arg(m_pClient->m_sName).arg(nRowCount));
    addLogMsg(enAlarm, A2T("客户端 %1 接收点表，行数 %2，剩余点 %3")
              .arg(m_pClient->m_sName).arg(nRowCount).arg(nRemainNum));

    //写本地临时文件
    handleWriteTagFile(sData);
    //全部发完后将临时文件改为正式文件
    if (0 == nRemainNum && false == sData.startsWith("#"))
    {
        m_pClient->m_bTagsReadReady = false;

        //确认路径是否存在
        QString sClientName = m_pClient->m_sName;
        QString sRootDir = QCoreApplication::applicationDirPath() + "/TagCfg/" + sClientName + "/";
        QString sTempFilePath = sRootDir + sClientName + "_temp.csv";   //临时文件路径
        QString sFilePath = sRootDir + sClientName + ".csv";            //正式文件路径
        if (false == QFile::exists(sTempFilePath))
        {
            return enFail;
        }

        //改旧文件名
        if (true == QFile::exists(sFilePath))
        {
            QString sNewFilePath = sRootDir + sClientName + "_" + QDateTime::currentDateTime().toString("yyMMdd_hhmmss") + ".csv";
            QFile fileOld(sFilePath);
            fileOld.rename(sNewFilePath);
        }

        //改新文件名
        QFile fileNew(sTempFilePath);
        fileNew.rename(sFilePath);

        //重读测点
        CPublicData::instance().reLoadClientTag(m_pClient);
        addLogMsg(enAlarm, A2T("客户端 %1 更新点表完成").arg(m_pClient->m_sName));

        m_pClient->m_bTagsReadReady = true;
    }
    return enSuccess;
}

int CClientProcess::handleWriteTagFile(const QString& sTagInfo)
{
    QString sClientName = m_pClient->m_sName;
    QString sTempFilePath = QCoreApplication::applicationDirPath() + "/TagCfg/"
            + sClientName + "/" + sClientName + "_temp.csv";

    //是否覆写
    bool bOverWrite = false;
    //如果注入信息是报文头，则判断本地临时文件是否存在
    if (true == sTagInfo.startsWith("#"))
    {
        //先打开确认文件头没有冲突
        QFile fileOld(sTempFilePath);
        if (true == fileOld.open(QFile::ReadOnly | QFile::Text))
        {
            QTextStream inOld(&fileOld);
            QString sText = inOld.readLine(1);
            if (true == sText.startsWith("#"))
                bOverWrite = true;
            fileOld.close();
        }
    }
    QIODevice::OpenMode nFlag = QFile::WriteOnly | QFile::Append;
    if (true == bOverWrite) nFlag = QFile::WriteOnly | QFile::Truncate;

    //写入文本
    QFile file(sTempFilePath);
    if (false == file.open(nFlag))
    {
        return enFail;
    }
    QTextStream inTemp(&file);
    inTemp << sTagInfo;
    file.close();
    return enSuccess;
}

int CClientProcess::processDataRealData(QByteArray &byteData, CKYTXHeader* pHeader)
{
    Q_UNUSED(pHeader)
    if (m_pClient == nullptr)   return sendRecvStatus(enFail, CKYTXBase::enCmdRealData);

    //取测点长度
    if (byteData.size() < (int)sizeof(quint16))
        return sendRecvStatus(enFail, CKYTXBase::enCmdRealData);

    quint16 nTagNum = getQuint16(byteData);
    byteData.remove(0, 2);
#if DEF_TEST == 0
    sendRecvStatus(enSuccess);
#endif

    //解析数据变化
    QMutexLocker locker(m_pClient->m_pMutex);
    int nPointCount = 0;
    while (false == byteData.isEmpty())
    {
        if (byteData.size() < (int)sizeof(CKYTXRealData) || nPointCount > nTagNum)
        {
            byteData.clear();
            continue;
        }
        //限制读取次数
        nPointCount++;
        if (nPointCount > m_pClient->getPointCount()) break;
        //根据索引取测点
        CKYTXRealData* pData = new CKYTXRealData;
        memcpy(pData, byteData, sizeof(CKYTXRealData));
        quint32 nIndex = pData->getIndex();
        CTagData* pTag = m_pClient->m_mapTag.value(nIndex);
        if (pTag == nullptr)
        {
            SAFE_DELETE(pData);
            byteData.remove(0, sizeof(CKYTXRealData));
            continue;
        }
        pTag->m_nQuality = pData->getQuality();

        //字符串型数据
        if (CTagData::enString == pTag->m_nDataType)
        {
            quint16 nStrLen = pData->getStrLength();
            //校验数据总长
            int nTotalStrLen = sizeof(CKYTXRealData) - sizeof(quint64) + nStrLen;
            if (byteData.size() < nTotalStrLen) continue;
            //取字符串
            QByteArray byteStr = byteData.mid(sizeof(CKYTXRealData) - sizeof(quint64), nStrLen);
            pTag->setString(byteStr);
            pTag->setTimeStamp(pData->getTime());
            byteData.remove(0, nTotalStrLen);
        }
        //整型和浮点型数据
        else
        {
            pTag->setBEData(pData->getData());
            pTag->setTimeStamp(pData->getTime());
            byteData.remove(0, sizeof(CKYTXRealData));
        }
        SAFE_DELETE(pData);
    }
#if DEF_TEST == 1
    QString sMsg = A2T("更新数量:%1").arg(nTagNum);
    sendRecvStatus(enSuccess, CKYTXBase::enCmdRealData, sMsg);
#endif
    return enSuccess;
}

int CClientProcess::processDataWarning(QByteArray &byteData, CKYTXHeader* pHeader)
{
    Q_UNUSED(pHeader)
    if (m_pClient == nullptr)   return sendRecvStatus(enFail);
    //验证报文长度
    quint16 nTotalSize = getQuint16(byteData);
    if (nTotalSize < 1)
        return sendRecvStatus(false);
    else
        sendRecvStatus(true);
    byteData.remove(0, 2);
    for (int i = 0; i < nTotalSize; i++)
    {
        quint16 nWarningSize = getQuint16(byteData);
        if (nWarningSize > 4096 || nWarningSize > byteData.size()) continue;
        QString sWarning = A2T("客户端警告：") + QString::fromLatin1(byteData.mid(0, nWarningSize));
        byteData.remove(0, nWarningSize);
        addLogMsg(enAlarm, sWarning);
    }
    return true;
}

int CClientProcess::processDataStatus(QByteArray &byteData, CKYTXHeader* pHeader)
{
    //验证报文长度
    if (byteData.size() < (int)sizeof(CKYTXStatus))
        return sendRecvStatus(enFail);
    CKYTXStatus* pStatus = new CKYTXStatus;
    memcpy(pStatus, byteData, sizeof(CKYTXStatus));
    byteData.remove(0, sizeof(CKYTXStatus));

    int nStatusSize = pStatus->getDriverNum() * 2;
    if (byteData.size() < nStatusSize)
        return sendRecvStatus(enFail);
    else
        sendRecvStatus(enSuccess);

    QMutexLocker locker(m_pClient->m_pMutex);
    m_pClient->m_dCPU = pStatus->getCPUUsage();
    m_pClient->m_dMemory = pStatus->getRAMUsage();
    m_pClient->m_dDiskUsage = pStatus->getDiskUsage();
    m_pClient->m_dTemperature = pStatus->getTemperature();

    SAFE_DELETE(pStatus);
    byteData.clear();
//    for (int i = 0; i < nStatusSize; i++)
//    {
//        //quint16 nStatus = getQuint16(byteData);
//        byteData.remove(0, 2);
//    }

    return true;
}

int CClientProcess::processDataHistory(QByteArray &byteData, CKYTXHeader* pHeader)
{
    //长度不足返回错误
    if (byteData.size() < sizeof(CKYTXHistory))
        return sendRecvStatus(enFail, CKYTXBase::enCmdHistory);
#if DEF_TEST == 0
    else
        sendRecvStatus(enSuccess, CKYTXBase::enCmdHistory);
#endif

    //解析历史信息报文
    CKYTXHistory* pHistory = new CKYTXHistory;
    memcpy(pHistory, byteData, sizeof(CKYTXHistory));
    byteData.remove(0, sizeof(CKYTXHistory));

    QVector<CTagData*> vecTagHistory;
    quint16 nSizeofData = sizeof(CKYTXRealData);
    while (byteData.size() >= nSizeofData)
    {
        CKYTXRealData* pData = new CKYTXRealData;
        memcpy(pData, byteData, sizeof(CKYTXRealData));
        quint32 nIndex = pData->getIndex();
        CTagData* pTag = m_pClient->m_mapTag.value(nIndex);
        if (pTag == nullptr)
        {
            SAFE_DELETE(pData);
            byteData.remove(0, nSizeofData);
            continue;
        }
        CTagData* pTagHistory = new CTagData();
        pTagHistory->copyTag(pTag);
        pTagHistory->m_nQuality = pData->nQuality;
        pTagHistory->setTimeStamp(pData->getTime());
        //字符串型
        if (CTagData::enString == pTagHistory->m_nDataSize)
        {
            quint16 nStrLen = pData->getStrLength();
            int nTotalStrLen = sizeof(CKYTXRealData) - sizeof(quint64) + nStrLen;
            if (byteData.size() < nTotalStrLen) continue;
            QByteArray byteStr = byteData.mid(sizeof(CKYTXRealData) - sizeof(quint64), nStrLen);
            pTag->setString(byteStr);
            byteData.remove(0, nTotalStrLen);
        }
        //数字型
        else
        {
            pTagHistory->setBEData(pData->getData());
            byteData.remove(0, nSizeofData);
        }
        vecTagHistory.append(pTagHistory);
    }
    if (vecTagHistory.size() > 0)
    {
        QMutexLocker locker(CPublicData::instance().m_pMutex);
        CPublicData::instance().m_vecTagHistory.append(vecTagHistory);
    }
#if DEF_TEST == 1
    QString sMsg = A2T("数量：%1").arg(vecTagHistory.size());
    sendRecvStatus(enSuccess, CKYTXBase::enCmdHistory, sMsg);
#endif
    return enSuccess;
}

int CClientProcess::processDataLog(QByteArray &byteData, CKYTXHeader* pHeader)
{
    if (m_pClient == nullptr)   return sendRecvStatus(enFail);
    //取报警数量
    if (byteData.size() < (int)sizeof(quint16))
        return sendRecvStatus(enFail);
    quint16 nTotalSize = getQuint16(byteData);
    if (nTotalSize < 1)
        return sendRecvStatus(enFail);
    else
        sendRecvStatus(enSuccess);
    byteData.remove(0, 2);
    for (int i = 0; i < nTotalSize; i++)
    {
        quint16 nWarningSize = getQuint16(byteData);
        if (nWarningSize > 4096) continue;
        QString sWarning = QString::fromLatin1(byteData.mid(0, nWarningSize));
        byteData.remove(0, nWarningSize);
        //emit sigClientLogMsg(m_stCfg.m_sGuid, sWarning);
    }
    return true;
}

quint16 CClientProcess::getQuint16(const QByteArray &byte, const int &nIndex)
{
    if (nIndex + 2 > byte.size() || nIndex < 0) return 0;
    const uchar* ptr = reinterpret_cast<const uchar*>(byte.constData() + nIndex);
    return qFromBigEndian<quint16>(ptr);
}

int CClientProcess::sendRecvStatus(int nSuccess, int nFunc, QString sVal)
{
    QString sMsg;
    if (nFunc > 0)
    {
        switch (nFunc) {
        case CKYTXBase::enCmdTags:
            sMsg = A2T("数据包：点表信息 ") + sVal;
            break;
        case CKYTXBase::enCmdRealData:
            sMsg = A2T("数据包：实时数据 ") + sVal;
            break;
        case CKYTXBase::enCmdHistory:
            sMsg = A2T("数据包：历史数据 ") + sVal;
            break;
        default:
            break;
        }
    }

    char szBuffer[2];
    memset(szBuffer, 0, 2);
    if (enSuccess == nSuccess || enWait == nSuccess)
    {
        szBuffer[0] = m_nReplyByte1;
        if (nFunc > 0) sMsg += A2T(" 回复：ff");
    }
    else
    {
        szBuffer[0] = m_nReplyByte0;
        if (nFunc > 0) sMsg += A2T(" 回复：00");
    }

    if (send(m_nClientFd, szBuffer, 1, MSG_NOSIGNAL) == -1)
    {
        if (nFunc > 0) addLogMsg(enWorkInfo, sMsg + A2T("发送失败"));
    }
    else
    {
        if (nFunc > 0) addLogMsg(enWorkInfo, sMsg);
    }
    return nSuccess;
}

void CClientProcess::addLogMsg(int nLevel, const QString &sMsg)
{
    emit sigLogMsg(m_nClientFd, nLevel, A2T("TCP服务端"), sMsg);
}

QMap<int, CClientProcess*> CEpollClientDataMgr::m_mapClient;
QMutex CEpollClientDataMgr::m_mutex;
